package chat_api

import (
	"encoding/json"
	"fmt"
	"gin_study_blog/common"
	"gin_study_blog/common/blog_type"
	"gin_study_blog/common/http"
	"gin_study_blog/models"
	"github.com/DanPlayer/randomname"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"github.com/sirupsen/logrus"
	netHttp "net/http"
	"strings"
	"time"
)

type ChatUser struct {
	Conn     *websocket.Conn
	NickName string `json:"nick_name"` // 随机生成
	Avatar   string `json:"avatar"`    // 头像
}

// 使用 map 就不会出现 defer后关闭 websocket 连接, map收集用户在线ip
var connGroupMaps = map[string]ChatUser{}

// MsgType msg 类型
const (
	InRoomMsg  blog_type.MsgType = 1 // 入参
	TextMsg    blog_type.MsgType = 2 // 文本
	ImageMsg   blog_type.MsgType = 3 // 图片
	VoiceMsg   blog_type.MsgType = 4 // 语音
	VideoMsg   blog_type.MsgType = 5 // 视频
	SystemMsg  blog_type.MsgType = 6 // 系统
	OutRoomMsg blog_type.MsgType = 7 // 退出聊天室
)

// GroupRequest 消息入参
type GroupRequest struct {
	Content string            `json:"content"`  // 聊天内容 如果是图片,就是传的数据流
	MsgType blog_type.MsgType `json:"msg_type"` // 聊天内容 类型
}

// GroupResponse 消息出参
type GroupResponse struct {
	NickName    string            `json:"nick_name"`     // 随机生成
	Avatar      string            `json:"avatar"`        // 头像
	MsgType     blog_type.MsgType `json:"msg_type"`      // 聊天内容 类型
	Content     string            `json:"content"`       // 聊天内容 如果是图片,就是传的数据流
	OnLineCount int               `json:"on_line_count"` // 在线人数
	Date        time.Time         `json:"date"`          // 消息时间
}

// ChatGroupView 群聊的雏形
func (ChatApi) ChatGroupView(c *gin.Context) {
	var upGrader = websocket.Upgrader{
		CheckOrigin: func(r *netHttp.Request) bool {
			// 鉴权 true 表示放行, false 表示拦截
			return true
		},
	}
	// 将http升级至websocket
	conn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		common.Log.Error(err.Error())
		http.FailWithCode(http.ParamsError, c)
		return
	}
	nickName := randomname.GenerateName()
	firstName := string([]rune(nickName)[0])
	avatar := fmt.Sprintf("E:\\project_Go\\src\\gin_study_blog\\uploads\\chat_avatar\\%s.png", firstName)
	addr := conn.RemoteAddr().String()
	chatUser := ChatUser{
		Conn:     conn,
		NickName: nickName,
		Avatar:   avatar,
	}
	connGroupMaps[addr] = chatUser
	// 需要生成昵称, 昵称和头像进行关联
	logrus.Infof("%s 连接成功", addr)
	for {
		// 消息类型, 消息, 错误
		_, p, err := conn.ReadMessage()
		if err != nil {
			// 用户断开聊天
			SendGroupMsg(conn, GroupResponse{
				NickName:    chatUser.NickName,
				Avatar:      chatUser.Avatar,
				Content:     fmt.Sprintf("%s 离开聊天室", chatUser.NickName),
				MsgType:     OutRoomMsg,
				Date:        time.Now(),
				OnLineCount: len(connGroupMaps) - 1,
			})
			break
		}
		// 进行参数绑定
		var request GroupRequest
		err = json.Unmarshal(p, &request)
		if err != nil {
			// 参数绑定失败
			SendMsg(addr, GroupResponse{
				NickName:    chatUser.NickName,
				Avatar:      chatUser.Avatar,
				MsgType:     SystemMsg,
				Content:     "参数绑定失败",
				OnLineCount: len(connGroupMaps),
			})
			continue
		}
		// 判断内容类型
		switch request.MsgType {
		case TextMsg:
			// 发送消息, 发送消息为空就continue
			if strings.TrimSpace(request.Content) == "" {
				SendMsg(addr, GroupResponse{
					NickName:    chatUser.NickName,
					Avatar:      chatUser.Avatar,
					MsgType:     SystemMsg,
					Content:     "消息不能为空",
					OnLineCount: len(connGroupMaps),
				})
				continue
			}
			SendGroupMsg(conn, GroupResponse{
				NickName:    chatUser.NickName,
				Avatar:      chatUser.Avatar,
				Content:     request.Content,
				MsgType:     TextMsg,
				Date:        time.Now(),
				OnLineCount: len(connGroupMaps),
			})
		case InRoomMsg:
			SendGroupMsg(conn, GroupResponse{
				NickName:    chatUser.NickName,
				Avatar:      chatUser.Avatar,
				MsgType:     TextMsg,
				Content:     fmt.Sprintf("%s 进入聊天室", chatUser.NickName),
				Date:        time.Now(),
				OnLineCount: len(connGroupMaps),
			})
		default:
			SendMsg(addr, GroupResponse{
				NickName:    chatUser.NickName,
				Avatar:      chatUser.Avatar,
				MsgType:     SystemMsg,
				Content:     "消息类型错误",
				OnLineCount: len(connGroupMaps),
			})
		}
	}
	defer conn.Close()
	delete(connGroupMaps, addr)
}

// SendGroupMsg 循环将消息发送到群中
func SendGroupMsg(conn *websocket.Conn, response GroupResponse) {
	byteData, _ := json.Marshal(response)
	_addr := conn.RemoteAddr().String()
	ip, addr := GetIpAndAddr(_addr)
	common.DB.Create(&models.ChatModel{
		NickName: response.NickName,
		Avatar:   response.Avatar,
		Content:  response.Content,
		IP:       ip,
		Addr:     addr,
		IsGroup:  true,
		MsgType:  response.MsgType,
	})
	for _, ChatUser := range connGroupMaps {
		ChatUser.Conn.WriteMessage(websocket.TextMessage, byteData)
	}
}

// SendMsg 给某一个用户发送
func SendMsg(_addr string, response GroupResponse) {
	byteData, _ := json.Marshal(response)
	chatUser := connGroupMaps[_addr]
	ip, addr := GetIpAndAddr(_addr)
	common.DB.Create(&models.ChatModel{
		NickName: response.NickName,
		Avatar:   response.Avatar,
		Content:  response.Content,
		IP:       ip,
		Addr:     addr,
		IsGroup:  false,
		MsgType:  response.MsgType,
	})
	chatUser.Conn.WriteMessage(websocket.TextMessage, byteData)
}

func GetIpAndAddr(addr string) (ip string, address string) {
	getIpList := strings.Split(addr, ":")
	address = "内网"
	return getIpList[0], address
}
